# 上市公司营收预测模型
# 导入必要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
import xgboost as xgb
import warnings
warnings.filterwarnings('ignore')

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 1. 数据加载与预处理
def load_and_preprocess_data(filepath):
    """
    加载并预处理数据
    """
    # 加载数据
    try:
        data = pd.read_excel(filepath)
        print("数据加载成功！")
        print(f"数据形状: {data.shape}")
        print("\n数据前5行:")
        print(data.head())
        
        # 检查缺失值
        print("\n缺失值情况:")
        print(data.isnull().sum())
        
        # 数据类型检查
        print("\n数据类型:")
        print(data.dtypes)
        
        return data
    except Exception as e:
        print(f"数据加载失败: {e}")
        return None

# 2. 探索性数据分析
def exploratory_data_analysis(data):
    """
    探索性数据分析
    """
    # 基本统计信息
    print("\n基本统计信息:")
    print(data.describe())
    
    # 相关性分析
    if len(data.select_dtypes(include=[np.number]).columns) > 1:
        plt.figure(figsize=(12, 10))
        correlation = data.corr()
        sns.heatmap(correlation, annot=True, cmap='coolwarm', linewidths=0.5)
        plt.title('特征相关性热力图')
        plt.tight_layout()
        plt.savefig('correlation_heatmap.png')
        plt.close()
        print("\n相关性热力图已保存为 'correlation_heatmap.png'")
    
    # 营收分布
    if '营业收入' in data.columns:
        plt.figure(figsize=(10, 6))
        # 使用distplot代替histplot以兼容旧版本seaborn
        sns.distplot(data['营业收入'], kde=True)
        plt.title('营收分布')
        plt.xlabel('营业收入')
        plt.ylabel('频数')
        plt.tight_layout()
        plt.savefig('revenue_distribution.png')
        plt.close()
        print("\n营收分布图已保存为 'revenue_distribution.png'")

# 3. 特征工程
def feature_engineering(data, target_column='营业收入'):
    """
    特征工程
    """
    # 创建副本
    df = data.copy()
    
    # 处理缺失值
    if df.isnull().sum().sum() > 0:
        # 数值型列用中位数填充
        num_cols = df.select_dtypes(include=[np.number]).columns
        for col in num_cols:
            if df[col].isnull().sum() > 0:
                df[col].fillna(df[col].median(), inplace=True)
        
        # 分类型列用众数填充
        cat_cols = df.select_dtypes(include=['object']).columns
        for col in cat_cols:
            if df[col].isnull().sum() > 0:
                df[col].fillna(df[col].mode()[0], inplace=True)
    
    # 处理分类变量
    cat_cols = df.select_dtypes(include=['object']).columns
    for col in cat_cols:
        if col not in ['证券代码', '证券简称', target_column]:  # 确保不是目标变量或ID列
            df = pd.get_dummies(df, columns=[col], drop_first=True)
    
    return df

# 4. 模型构建与评估
def build_and_evaluate_models(data, target_column='营业收入'):
    """
    构建并评估多种预测模型
    """
    # 准备数据
    X = data.drop(columns=[target_column, '证券代码', '证券简称'])  # 移除非数值特征
    y = data[target_column]
    
    # 特征缩放
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
    
    # 定义模型
    models = {
        '线性回归': LinearRegression(),
        '岭回归': Ridge(),
        'Lasso回归': Lasso(),
        '随机森林': RandomForestRegressor(random_state=42),
        '梯度提升': GradientBoostingRegressor(random_state=42),
        'XGBoost': xgb.XGBRegressor(random_state=42)
    }
    
    # 评估指标
    results = {
        'Model': [],
        'RMSE': [],
        'MAE': [],
        'R²': []
    }
    
    # 训练和评估模型
    for name, model in models.items():
        print(f"\n训练模型: {name}")
        
        # 训练模型
        model.fit(X_train, y_train)
        
        # 预测
        y_pred = model.predict(X_test)
        
        # 计算评估指标
        rmse = np.sqrt(mean_squared_error(y_test, y_pred))
        mae = mean_absolute_error(y_test, y_pred)
        r2 = r2_score(y_test, y_pred)
        
        # 保存结果
        results['Model'].append(name)
        results['RMSE'].append(rmse)
        results['MAE'].append(mae)
        results['R²'].append(r2)
        
        # 打印结果
        print(f"{name} - RMSE: {rmse:.4f}, MAE: {mae:.4f}, R²: {r2:.4f}")
    
    # 转换为DataFrame
    results_df = pd.DataFrame(results)
    
    # 可视化模型性能
    plt.figure(figsize=(12, 8))
    
    plt.subplot(2, 1, 1)
    sns.barplot(x='Model', y='RMSE', data=results_df)
    plt.title('模型RMSE比较')
    plt.xticks(rotation=45)
    
    plt.subplot(2, 1, 2)
    sns.barplot(x='Model', y='R²', data=results_df)
    plt.title('模型R²比较')
    plt.xticks(rotation=45)
    
    plt.tight_layout()
    plt.savefig('model_performance.png')
    plt.close()
    print("\n模型性能比较图已保存为 'model_performance.png'")
    
    return results_df, models, X_train, X_test, y_train, y_test, X, y

# 5. 特征重要性分析
def analyze_feature_importance(models, X, y):
    """
    分析特征重要性
    """
    # 使用随机森林模型分析特征重要性
    rf_model = models['随机森林']
    
    # 获取特征重要性
    importances = rf_model.feature_importances_
    feature_names = X.columns
    
    # 创建DataFrame
    feature_importance_df = pd.DataFrame({
        'Feature': feature_names,
        'Importance': importances
    }).sort_values(by='Importance', ascending=False)
    
    # 可视化前10个重要特征
    plt.figure(figsize=(12, 8))
    top_features = feature_importance_df.head(10)
    sns.barplot(x='Importance', y='Feature', data=top_features)
    plt.title('特征重要性 (Top 10)')
    plt.tight_layout()
    plt.savefig('feature_importance.png')
    plt.close()
    print("\n特征重要性图已保存为 'feature_importance.png'")
    
    return feature_importance_df

# 6. 模型优化
def optimize_best_model(models, X_train, X_test, y_train, y_test):
    """
    优化最佳模型
    """
    # 假设随机森林是最佳模型，进行参数优化
    print("\n优化随机森林模型...")
    
    # 定义参数网格
    param_grid = {
        'n_estimators': [50, 100, 200],
        'max_depth': [None, 10, 20, 30],
        'min_samples_split': [2, 5, 10],
        'min_samples_leaf': [1, 2, 4]
    }
    
    # 创建网格搜索对象
    grid_search = GridSearchCV(
        estimator=RandomForestRegressor(random_state=42),
        param_grid=param_grid,
        cv=5,
        scoring='neg_mean_squared_error',
        n_jobs=-1,
        verbose=1
    )
    
    # 执行网格搜索
    grid_search.fit(X_train, y_train)
    
    # 获取最佳模型
    best_rf = grid_search.best_estimator_
    
    # 使用最佳模型进行预测
    y_pred = best_rf.predict(X_test)
    
    # 计算评估指标
    rmse = np.sqrt(mean_squared_error(y_test, y_pred))
    mae = mean_absolute_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    
    print(f"\n优化后的随机森林模型 - RMSE: {rmse:.4f}, MAE: {mae:.4f}, R²: {r2:.4f}")
    print(f"最佳参数: {grid_search.best_params_}")
    
    return best_rf, grid_search.best_params_

# 7. 生成报告
def generate_report(results_df, feature_importance_df, best_params):
    """
    生成实战报告
    """
    report = """
    # 上市公司营收预测模型实战报告

    ## 一、案例介绍
    在金融领域，上市公司营收预测非常重要，因为它直接反映了公司的运营情况和未来发展趋势。
    投资者和金融数据分析师通常关注上市公司营收预测，以评估公司业绩表现和投资潜力。

    ## 二、实战目标
    建立上市公司营收预测模型，评价模型的性能，探索影响上市公司营收的关键因素。

    ## 三、实战任务与结果

    ### 1. 设计指标，收集与整理数据
    - 数据来源：上市公司营收数据.xlsx
    - 数据预处理：处理缺失值、异常值，进行特征工程
    - 探索性数据分析：分析数据分布、相关性等

    ### 2. 建立预测模型及性能评价
    建立了以下六种预测模型：

    """
    
    # 添加模型性能表格
    report += "| 模型 | RMSE | MAE | R² |\n"
    report += "|------|------|-----|----|\n"
    for _, row in results_df.iterrows():
        report += f"| {row['Model']} | {row['RMSE']:.4f} | {row['MAE']:.4f} | {row['R²']:.4f} |\n"
    
    report += """

    ### 3. 最终预测模型确定
    根据模型性能比较，选择随机森林模型作为最终预测模型，并进行参数优化。

    优化后的最佳参数：
    """
    
    # 添加最佳参数
    for param, value in best_params.items():
        report += f"- {param}: {value}\n"
    
    report += """

    ### 4. 影响上市公司营收的关键因素
    根据特征重要性分析，影响上市公司营收的关键因素如下：

    """
    
    # 添加前10个重要特征
    top_features = feature_importance_df.head(10)
    report += "| 特征 | 重要性 |\n"
    report += "|------|--------|\n"
    for _, row in top_features.iterrows():
        report += f"| {row['Feature']} | {row['Importance']:.4f} |\n"
    
    report += """

    ## 四、结论与建议
    1. 通过建立多种预测模型，我们发现随机森林模型在预测上市公司营收方面表现最佳。
    2. 影响上市公司营收的关键因素包括[根据实际数据填写]。
    3. 建议投资者和分析师关注这些关键因素，以更准确地评估公司业绩和投资潜力。

    ## 五、模型局限性
    1. 模型基于历史数据，可能无法完全预测未来的市场变化。
    2. 外部因素如政策变化、市场环境等可能影响模型准确性。
    3. 数据质量和完整性对模型性能有重要影响。
    """
    
    # 保存报告
    with open('revenue_prediction_report.md', 'w', encoding='utf-8') as f:
        f.write(report)
    
    print("\n实战报告已保存为 'revenue_prediction_report.md'")
    
    return report

# 主函数
def main():
    # 数据文件路径
    data_path = 'c:\\Users\\YEDX\\e3\\上市公司营收数据.xlsx'
    
    # 1. 数据加载与预处理
    data = load_and_preprocess_data(data_path)
    if data is None:
        return
    
    # 2. 探索性数据分析
    exploratory_data_analysis(data)
    
    # 3. 特征工程
    processed_data = feature_engineering(data, target_column='营业收入')
    
    # 4. 模型构建与评估
    results_df, models, X_train, X_test, y_train, y_test, X, y = build_and_evaluate_models(processed_data)
    
    # 5. 特征重要性分析
    feature_importance_df = analyze_feature_importance(models, X, y)
    
    # 6. 模型优化
    best_model, best_params = optimize_best_model(models, X_train, X_test, y_train, y_test)
    
    # 7. 生成报告
    report = generate_report(results_df, feature_importance_df, best_params)
    
    print("\n实战任务完成！")

if __name__ == "__main__":
    main()